package com.pactera.madp.cp.service.sync.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pactera.madp.admin.api.constant.SyncConstant;
import com.pactera.madp.cp.api.bo.SyncWxOrgBo;
import com.pactera.madp.cp.api.entity.CorpEntity;
import com.pactera.madp.cp.api.entity.sync.SyncOrgInfoEntity;
import com.pactera.madp.cp.api.entity.sync.SyncWxOrgRelation;
import com.pactera.madp.cp.common.config.FileUploadConfig;
import com.pactera.madp.cp.common.constant.Constants;
import com.pactera.madp.cp.common.util.RedisUtil;
import com.pactera.madp.cp.common.util.WxApiUtils;
import com.pactera.madp.cp.context.SyncStateContext;
import com.pactera.madp.cp.mapper.sync.SyncOrgInfoMapper;
import com.pactera.madp.cp.mapper.sync.SyncWxOrgRelationMapper;
import com.pactera.madp.cp.service.ICorpService;
import com.pactera.madp.cp.service.sync.HrOrgSyncBusinessService;
import com.pactera.madp.cp.service.sync.SyncWxOrgRelationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class HrOrgSyncBusinessServiceImpl implements HrOrgSyncBusinessService{

    private final SyncWxOrgRelationMapper syncWxOrgRelationMapper;
    private final SyncOrgInfoMapper syncOrgInfoMapper;
    private final FileUploadConfig config;
    private final ICorpService corpService;
    private final SyncWxOrgRelationService syncWxOrgRelationService;
    private final RedisTemplate redisTemplate;

    @Override
    public Boolean syncBusiness() {
        String nowDate = DateUtil.format(new Date(), Constants.DATE_FORMAT);

        // 执行同步业务
        String lockKey = RedisUtil.buildKey(Constants.SYNC_ORG_BUSINESS_ORG2WX_LOCK, nowDate);
        try {
            // 获取同步业务的锁
            boolean isLocked = RedisUtil.getLock(lockKey, "1", Constants.EXPIRE_TIME, TimeUnit.SECONDS);
            if (!isLocked) {
                SyncStateContext.get().setSaveLog(false);
                return false;
            }
            log.info("机构业务同步获取锁成功");

            // 校验上次执行结果
            checkLastExec();
            // 获取当天机构列表
            List<SyncOrgInfoEntity> orgList = syncOrgInfoMapper.selectList(Wrappers.<SyncOrgInfoEntity>lambdaQuery()
                    .eq(SyncOrgInfoEntity::getDelOrgFlag, Constants.SYNC_DEL_FLAG));
            // 转换为业务对象
            List<SyncWxOrgBo> boList = convertEntityToBo(orgList);
            // 构建树
            SyncWxOrgBo rootBo = buildTree(boList);
            // 获取关联
            List<SyncWxOrgRelation> relationList = syncWxOrgRelationMapper.selectList(Wrappers.lambdaQuery());
            // 获取关联中最大企业微信部门id
            Integer maxWxDeptId = syncWxOrgRelationMapper.getMaxWxDeptId();
            maxWxDeptId = maxWxDeptId == null ? 1 : ++maxWxDeptId;
            // 构建企业微信部门id
            setRelation(rootBo, relationList, maxWxDeptId, 0);
            // 临时保存关联
            List<String> relList = new ArrayList<>();
            SyncWxOrgRelation rel = new SyncWxOrgRelation();
            List<SyncWxOrgBo> orderBoList = new ArrayList<>();
            tempSaveRelation(rootBo, relList, rel, orderBoList);
            // 缓存有序的新增更新列表
            cacheNewAndUpdateList(orderBoList);
            // 设置并缓存删除列表
            setAndCacheDelList(relationList, orderBoList);
            // 构建批量处理文件
            File csvFile = buildBatchCsv(null, rootBo);
            // 获取企业
            CorpEntity corp = corpService.list().get(0);
            // 上传素材获取media_id
            String mediaId = WxApiUtils.uploadImageToTemps(corp.getCorpId(), csvFile, "file");
            // 执行批量任务
            executeBatchTask(corp, mediaId);

            return Boolean.TRUE;
        } catch (Exception e) {
            // 清除关联缓存
            String key = RedisUtil.buildKey(Constants.SYNC_ORG_WX_RELATION, DateUtil.format(new Date(), Constants.DATE_FORMAT));
            redisTemplate.delete(key);
            throw new RuntimeException(e);
        } finally {
            redisTemplate.delete(lockKey);
            log.info("机构业务同步释放锁成功");
        }
    }

    // 校验上次执行结果
    private void checkLastExec() {
        // 检测上次回调是否成功
        Object o = redisTemplate.opsForValue().get(Constants.SYNC_CB_FLAG);
        if (o != null) {
            // 回调成功,清除状态
            redisTemplate.delete(Constants.SYNC_CB_FLAG);
            return;
        }
        // 回调没有成功,获取上次关联列表
        String key = RedisUtil.buildKey(Constants.SYNC_ORG_WX_RELATION, "*");
        Set<String> keys = redisTemplate.keys(key);
        if (keys.isEmpty()) {
            return;
        }
        key = keys.iterator().next();
        Long size = redisTemplate.opsForList().size(key);
        if (size == null || size == 0) {
            return;
        }
        // 保存上次关联
        List<String> list = redisTemplate.opsForList().range(key, 0, size);
        List<SyncWxOrgRelation> relList = new ArrayList<>();
        list.forEach(s -> relList.add(JSONUtil.toBean(s, SyncWxOrgRelation.class)));
        syncWxOrgRelationService.remove(Wrappers.emptyWrapper());
        syncWxOrgRelationService.saveBatch(relList);
        redisTemplate.delete(key);
        log.info("保存上一次企业微信部门与机构关联成功");
    }

    // 转换为业务对象
    private List<SyncWxOrgBo> convertEntityToBo(List<SyncOrgInfoEntity> orgList) {
        SyncWxOrgBo prototype = new SyncWxOrgBo();
        List<SyncWxOrgBo> boList = new ArrayList<>();
        for (SyncOrgInfoEntity org : orgList) {
            SyncWxOrgBo bo = prototype.clone();
            bo.setHrOrgId(org.getId());
            bo.setOrgName(org.getOrgName());
            bo.setOrgParentId(org.getOrgGroupCode());
            bo.setOrgAddr(org.getOrgAddr());
            // 如果是根节点机构，则设置对应企业微信根部门
            if (StrUtil.equals(org.getOrgGroupCode(), "-1")) {
                bo.setWxDeptId(1);
                bo.setWxParentId(0);
            }
            boList.add(bo);
        }
        return boList;
    }

    // 构建机构树
    private SyncWxOrgBo buildTree(List<SyncWxOrgBo> boList) {
        SyncWxOrgBo root = null;
        for (SyncWxOrgBo bo : boList) {
            boolean isSub = false;
            for (SyncWxOrgBo comBo : boList) {
                if (StrUtil.equals(bo.getHrOrgId(), comBo.getHrOrgId())) {
                    continue;
                }
                if (StrUtil.equals(bo.getOrgParentId(), comBo.getHrOrgId())) {
                    if (ObjectUtil.isNull(comBo.getSubList())) {
                        comBo.setSubList(new ArrayList<>());
                    }
                    comBo.getSubList().add(bo);
                    isSub = true;
                }
            }
            if (!isSub && bo.getWxDeptId() == 1) {
                root = bo;
            }
        }
        return root;
    }

    // 设置关联
    private int setRelation(SyncWxOrgBo root, List<SyncWxOrgRelation> relationList, int maxWxDeptId, int wxParentId) {
        log.info("机构与企业微信关联数：{}", relationList.size());
        // 是否关联标识
        boolean isRel = false;
        for (SyncWxOrgRelation rel : relationList) {
            if (StrUtil.equals(root.getHrOrgId(), rel.getHrOrgId())) {
                root.setWxDeptId(rel.getWxDeptId());
                root.setWxParentId(wxParentId);
                isRel = true;
            }
        }
        if (!isRel) {
            // 没有关联，为企业微信部门生成id
            root.setWxDeptId(maxWxDeptId++);
            root.setWxParentId(wxParentId);
        }
        List<SyncWxOrgBo> subList = root.getSubList();
        if (ObjectUtil.isNotNull(subList)) {
            for (SyncWxOrgBo bo : subList) {
                maxWxDeptId = setRelation(bo, relationList, maxWxDeptId, root.getWxDeptId());
            }
        }
        return maxWxDeptId;
    }

    // 临时保存关联
    private List<SyncWxOrgBo> tempSaveRelation(SyncWxOrgBo rootBo, List<String> relationList, SyncWxOrgRelation relation, List<SyncWxOrgBo> boList) {
        SyncWxOrgRelation rel = relation.clone();
        rel.setHrOrgId(rootBo.getHrOrgId());
        rel.setWxDeptId(rootBo.getWxDeptId());
        relationList.add(JSONUtil.toJsonStr(rel));
        boList.add(rootBo);

        List<SyncWxOrgBo> subList = rootBo.getSubList();
        if (ObjectUtil.isNotNull(subList)) {
            subList.forEach(bo -> {
                tempSaveRelation(bo, relationList, relation, boList);
            });
        }
        if (rootBo.getWxDeptId() == 1 && !relationList.isEmpty()) {
            String key = RedisUtil.buildKey(Constants.SYNC_ORG_WX_RELATION, DateUtil.format(new Date(), Constants.DATE_FORMAT));
            redisTemplate.delete(key);
            redisTemplate.opsForList().leftPushAll(key, relationList);
            log.info("机构与企业微信部门的关联临时保存成功");
        }
        return boList;
    }

    private void cacheNewAndUpdateList(List<SyncWxOrgBo> orderBoList) {
        if (orderBoList.isEmpty()) {
            return;
        }
        String key = RedisUtil.buildKey(Constants.SYNC_ORG_ORDER_LIST, DateUtil.format(new Date(), Constants.DATE_FORMAT));
        redisTemplate.delete(key);
        redisTemplate.opsForList().leftPushAll(key, orderBoList);
        log.info("缓存新增和更新机构数：{}", orderBoList.size());
    }

    // 设置并缓存删除列表
    private void setAndCacheDelList(List<SyncWxOrgRelation> relationList, List<SyncWxOrgBo> orderBoList) {
        List<SyncWxOrgRelation> delList = new ArrayList<>();

        boolean isDel;
        SyncWxOrgRelation tempRel;

        for (int i=relationList.size() - 1; i>=0; i--) {
            isDel = true;
            tempRel = relationList.get(i);
            for (SyncWxOrgBo bo : orderBoList) {
                if (relationList.get(i).getHrOrgId().equals(bo.getHrOrgId())) {
                    isDel = false;
                    break;
                }
            }
            if (isDel) {
                delList.add(tempRel);
            }
        }
        // 保存删除列表
        String key = RedisUtil.buildKey(SyncConstant.SYNC_WX_ORG_DEL_LIST, DateUtil.format(new Date(), Constants.DATE_FORMAT));
        if (delList.isEmpty()) {
            return;
        }
        redisTemplate.opsForList().leftPushAll(key, delList);
        log.info("缓存删除机构列表：{}", JSONUtil.toJsonStr(delList));
    }

    // 构建批量执行的csv文件
    private File buildBatchCsv(List<String> dataList, SyncWxOrgBo rootBo) {
        if (ObjectUtil.isNull(dataList)) {
            dataList = new ArrayList<>();
            dataList.add(Constants.SYNC_ORG_CSV_TEMPLATE);
        }
        String data = StrUtil.format("{},{},{},{}", rootBo.getOrgName(), rootBo.getWxDeptId(), rootBo.getWxParentId(), 1);
        dataList.add(data);
        // 遍历子列表
        List<SyncWxOrgBo> subList = rootBo.getSubList();
        if (ObjectUtil.isNotNull(subList)) {
            for (SyncWxOrgBo bo : subList) {
                buildBatchCsv(dataList, bo);
            }
        }
        // 如果是根节点则生成文件
        if (rootBo.getWxParentId() == 0) {
            String filePath = StrUtil.format("{}{}{}{}", config.getRealPath(), File.separator,
                    DateUtil.format(new Date(), Constants.DATE_FORMAT), "_batch_party.csv");
            File csvFile = FileUtil.writeUtf8Lines(dataList, filePath);
            log.info("生成机构csv文件成功");
            return csvFile;
        }
        return null;
    }

    // 批量异步同步企业微信部门
    private String executeBatchTask(CorpEntity corp, String mediaId) {
        Map<String,String> callback = new HashMap<>();
        callback.put("url", corp.getEventCallback());
        callback.put("token", corp.getToken());
        callback.put("encodingaeskey", corp.getEncodingAesKey());
        Map<String,Object> body = new HashMap<>();
        body.put("media_id", mediaId);
        body.put("callback", callback);

        String jobId = WxApiUtils.asyncBatchParty(corp.getCorpId(), JSONUtil.toJsonStr(body));
        if (StrUtil.isEmpty(jobId)) {
            throw new RuntimeException("文件异步全量机构同步企业微信部门任务执行失败");
        }
        log.info("文件异步全量机构同步到企业微信部门任务执行完毕");
        return jobId;
    }

}
